package cn.txun.csmall.product.aspect;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Slf4j
@Component
@Aspect//切面类，类中的方法可以作用于指定的方法
public class TimeAspect {

    //连接点(JoinPoint):程序执行过程中的某个节点，可能是某个方法的调用或者抛出的某个异常
    //切入点（pointcut）:选择一个或者多个连接点的表达式
    //execution配置的就是切入点表达式，用于匹配某些方法
    //在切入点表达式中，可以使用通配符
    //--1个星号：任意1次匹配
    //--两个连续的小数点：任意n次匹配,只能用于包名或者参数列表
    //                 *返回值
    //                   ---------包名----------
    //                                           *类名
    //                                             *方法名
    //                                               ..参数列表
    //另外，在表达式中，在方法的返回值的左侧还可以指定修饰符，修饰符是可选的
    //注意：注解也是典型修饰符之一
    //
    //通知（Advice）注解
    // @Around：环绕，包裹了连接点，你的代码可以在连接点之前或者之后执行
    // @Before：只能在连接点之前执行
    // @After:只能在连接点之后执行
    // @AfterThrowing
    //@AfterReturning

    /**
     * @Around
     * try{
     *   @Before
     *   执行连接点
     *   @AfterReturning
     * }catch(){
     *  @AfterThrowing
     * }finally{
     *   @After
     * }
     * @Around
     */

    @Around("execution(* cn.txun.csmall.product.service.*.*(..))")
    public Object xxx(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.currentTimeMillis();
        //获取匹配方法的相关信息
        //获取匹配方法所在类的类名
        String targetClassName = pjp.getTarget().getClass().getName();
        String signatureName = pjp.getSignature().getName();//获取匹配方法的方法名
        Object[] args = pjp.getArgs();//获取匹配方法参数列表
        //执行以上表达式匹配的方法，即某个Service的某个方法
        //注意1：必须获取调用proceed方法的返回的结果，作为当前切面方法的返回值
        //--如果没有获取，或者没有作为当前切面方法的返回值，相当于执行了连接点方法，却没有获取方法的返回值
        //注意2：调用proceed时的异常必须抛出，否则，controller将无法知晓Service抛出过异常
        //     就无法想客户端响应错误信息
        // 前提：本例的切面是作用于service，
        //其实，你也可以使用try...catch捕获proceed方法的异常，但必须在catch中抛出异常
        Object result = pjp.proceed();

        long end = System.currentTimeMillis();
        log.debug("{}.{}.参数列表{}执行时间:{}毫秒",
                targetClassName,signatureName, Arrays.toString(args),
                end-start);
        //一定返回调用proceed方法得到的记过
        return result;
    }
}
